﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Collections.ObjectModel;
using System.Xml;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

namespace RazorSourceGenerator
{
    [XmlRoot(ElementName = "RazorGeneratorConfig")]
    public class GeneratorConfig : ObservableObject
    {
        private string _connectionString;
        private string _workDir;
        private string _tableSchema;
        private string _tableCatalog;
        private string _tableType;
        private string _tableNames;
        private string _tableFilter;
        private string _commentSchema;

        public GeneratorConfig()
        {
            Templates = new ObservableCollection<TemplateConfig>();
        }

        [Required]
        [RequiredPathExists]
        public string WorkDir
        {
            get
            {
                if (_workDir == ".")
                    _workDir = Environment.CurrentDirectory;
                else if (_workDir != null && _workDir.StartsWith(".\\"))
                    _workDir = System.IO.Path.Combine(Environment.CurrentDirectory, _workDir.TrimStart('.', '\\'));

                return _workDir;
            }
            set
            {
                if (_workDir != value)
                {
                    _workDir = value;
                    RaisePropertyChanged("WorkDir");
                }
            }
        }
        [Required]
        public string ConnectionString 
        {
            get { return _connectionString; }
            set
            {
                if (_connectionString != value)
                {
                    _connectionString = value;
                    RaisePropertyChanged("ConnectionString");
                }
            }
        }        
        public string TableSchema
        {
            get { return _tableSchema; }
            set
            {
                if (_tableSchema != value)
                {
                    _tableSchema = value;
                    RaisePropertyChanged("TableSchema");
                }
            }
        }
        public string CommentSchema
        {
            get { return _commentSchema; }
            set
            {
                if (_commentSchema != value)
                {
                    _commentSchema = value;
                    RaisePropertyChanged("TableSchema");
                }
            }
        }
        public string TableCatalog
        {
            get { return _tableCatalog; }
            set
            {
                if (_tableCatalog != value)
                {
                    _tableCatalog = value;
                    RaisePropertyChanged("TableCatalog");
                }
            }
        }
        public string TableType
        {
            get { return _tableType; }
            set
            {
                if (_tableType != value)
                {
                    _tableType = value;
                    RaisePropertyChanged("TableType");
                }
            }
        }
        public string TableNames
        {
            get { return _tableNames; }
            set
            {
                if (_tableNames != value)
                {
                    _tableNames = value;
                    RaisePropertyChanged("TableNames");
                }
            }
        }
        public string TableFilter
        {
            get { return _tableFilter; }
            set
            {
                if (_tableFilter != value)
                {
                    _tableFilter = value;
                    RaisePropertyChanged("TableFilter");
                }
            }
        }
        
        [XmlArrayItem(ElementName="Template", Type=typeof(TemplateConfig))]
        public ObservableCollection<TemplateConfig> Templates { get; set; }
    }

    public class TemplateConfig : ObservableObject
    {
        private string _name;
        private string _fileName;
        private string _outputFileName;
        private string _sql;
        private bool _loopInSQLResult;
        private bool _enabled;
        private bool _clearOutputDir;
        private bool _append;
        private bool _isIncluding;
        private string _encoding;

        public TemplateConfig()
        {
            _enabled = true;
        }

        [XmlAttribute]
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    RaisePropertyChanged("Name");
                }
            }
        }
        [Required]
        public string FileName
        {
            get 
            {
                return _fileName; 
            }
            set
            {
                if (_fileName != value)
                {
                    _fileName = value;
                    RaisePropertyChanged("FileName");
                }
            }
        }
        public bool IsIncluding
        {
            get
            {
                return _isIncluding;
            }
            set
            {
                if (_isIncluding != value)
                {
                    _isIncluding = value;
                    RaisePropertyChanged("Tip");
                    RaisePropertyChanged("IsIncluding");
                }
            }
        }
        //[Required]
        public string OutputFileName
        {
            get { return _outputFileName; }
            set
            {
                if (_outputFileName != value)
                {
                    _outputFileName = value;
                    RaisePropertyChanged("OutputFileName");
                }
            }
        }
        [XmlIgnore]
        public string SQL
        {
            get { return _sql; }
            set 
            {
                if (_sql != value)
                {
                    _sql = value;
                    RaisePropertyChanged("SQL");
                    RaisePropertyChanged("Tip");
                    RaisePropertyChanged("TipSQL");
                }
            }
        }
        public bool LoopInSQLResult
        {
            get { return _loopInSQLResult; }
            set 
            {
                if (_loopInSQLResult != value)
                {
                    _loopInSQLResult = value;
                    RaisePropertyChanged("LoopInSQLResult");
                }
            }
        }
        [XmlElement("SQL")]
        public XmlNode SQLNode
        {
            get
            {
                var dom = new XmlDocument();
                return dom.CreateCDataSection(this.SQL);
            }
            set
            {
                if (value != null)
                {
                    this.SQL = value.Value;
                }
                else
                {
                    this.SQL = null;
                }
            }
        }
        public bool Enabled
        {
            get { return _enabled; }
            set
            {
                if (_enabled != value)
                {
                    _enabled = value;
                    RaisePropertyChanged("Enabled");
                }
            }
        }
        public bool ClearOutputDir
        {
            get { return _clearOutputDir; }
            set
            {
                if (_clearOutputDir != value)
                {
                    _clearOutputDir = value;
                    RaisePropertyChanged("ClearOutputDir");
                }
            }
        }
        public bool Append
        {
            get { return _append; }
            set
            {
                if (_append != value)
                {
                    _append = value;
                    RaisePropertyChanged("Append");
                }
            }
        }
        public string Encoding
        {
            get { return _encoding; }
            set
            {
                if (_encoding != value)
                {
                    _encoding = value;
                    RaisePropertyChanged("Encoding");
                }
            }
        }
        [XmlIgnore]
        public string Tip
        {
            get 
            {
                if (_isIncluding)
                    return "(Including)";
                return string.IsNullOrEmpty(_sql) ? "(GenBySchema)" : "(GenBySQL)";
            }
            private set {}
        }
        [XmlIgnore]
        public string TipSQL
        {
            get
            {
                return string.IsNullOrEmpty(_sql) ? "SQL(null)" : "SQL(...)";
            }
            private set {}
        }
    }

    public class ObservableObject : INotifyPropertyChanged, IDataErrorInfo
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public virtual void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public virtual string Error
        {
            get 
            {
                var res = new List<ValidationResult>();
                var result = Validator.TryValidateObject(this, new ValidationContext(this, null, null), res);
                return res.Count > 0 ? string.Join(Environment.NewLine, res.Select(r => r.ErrorMessage).ToArray()) : "";
            }
        }

        public virtual string this[string columnName]
        {
            get 
            {
                var vc = new ValidationContext(this, null, null);
                vc.MemberName = columnName;
                var res = new List<ValidationResult>();
                var result = Validator.TryValidateProperty(this.GetType().GetProperty(columnName).GetValue(this, null), vc, res);
                if (res.Count > 0)
                {
                    //RaisePropertyChanged("Error");
                    return string.Join(Environment.NewLine, res.Select(r => r.ErrorMessage).ToArray());
                }
                return string.Empty;
            }
        }

        private static ValidationAttribute[] GetValidations(PropertyInfo property)
        {
            return (ValidationAttribute[])property.GetCustomAttributes(typeof(ValidationAttribute), true);
        }
    }

}